﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Security;
using TestLink2Net.Bal;
using System.Security.Permissions;
using System.Threading;
using System.Security.Principal;

namespace TestLink2Net.Security
{
    public class RightPermission : IPermission, ICloneable, IEquatable<RightPermission>
    {
        private String _userName;
        private String _rightName;
        private Boolean _authenticated;

        public Boolean Authenticated
        {
            get { return _authenticated; }
            set { _authenticated = value; }
        }

        public String RightName
        {
            get { return _rightName; }
            set { _rightName = value; }
        }

        public String UserName
        {
            get { return _userName; }
            set { _userName = value; }
        }

        private RightPermission()
            : this(false, String.Empty, String.Empty)
        {
        }

        public RightPermission(Boolean authenticated, String userName, String rightName)
        {
            _authenticated = authenticated;
            _userName = userName;
            _rightName = rightName;
        }

        #region IEquatable<RightPermission> Members

        public bool Equals(RightPermission other)
        {
            if (this.Authenticated != other.Authenticated) return false;
            if (this.UserName != other.UserName) return false;
            if (this.RightName != other.RightName) return false;
            return true;
        }

        #endregion

        private Boolean IsUnrestircted()
        {
            if (_authenticated) return false;
            if (!String.IsNullOrEmpty(_userName)) return false;
            if (!String.IsNullOrEmpty(_rightName)) return false;
            return true;
        }

        private void DenyAccess()
        {
            DenyAccess("You lack the rights to do this");
        }
        private void DenyAccess(String message)
        {
            throw new SecurityException(message);
        }
        #region IPermission Members

        public IPermission Copy()
        {
            return this.Clone();
        }

        public void Demand()
        {
            if (IsUnrestircted()) return;
            IPrincipal user = Thread.CurrentPrincipal;
            if ((_authenticated) && (!user.Identity.IsAuthenticated)) DenyAccess("You must be logged in to do this.");
            if ((!String.IsNullOrEmpty(_userName)) && (_userName != user.Identity.Name)) DenyAccess("Your account lacks the rights to do this.");
            if (!String.IsNullOrEmpty(_rightName))
            {
                if (!(user is RightPrincipal)) DenyAccess();
                if (!((RightPrincipal)user).HasRight(_rightName)) DenyAccess();
            }
        }

        public IPermission Intersect(IPermission target)
        {
            RightPermission result = null;
            Boolean hasIntersection = true;
            if (target != null)
            {
                if (!(target is RightPermission)) throw new ArgumentException("Target was not a RightPermission");

                RightPermission p = (RightPermission)target;

                if (this.Equals(p))
                    result = Clone();
                else
                {
                    result = new RightPermission();

                    result.Authenticated = (_authenticated || p.Authenticated);
                    if (_userName == p.UserName) result.UserName = _userName;
                    else if (String.IsNullOrEmpty(_userName)) result.UserName = p.UserName;
                    else if (String.IsNullOrEmpty(p.UserName)) result.UserName = _userName;
                    else hasIntersection = false;


                    if (_rightName == p.RightName) result.RightName = _rightName;
                    else if (String.IsNullOrEmpty(_rightName)) result.RightName = p.RightName;
                    else if (String.IsNullOrEmpty(p.RightName)) result.RightName = _rightName;
                    else hasIntersection = false;
                }
            }
            if (!hasIntersection) result = null;
            return result;

        }

        private Boolean IsSubsetOf(RightPermission target)
        {
            Boolean result = false;
            if (Equals(target))
                result = true;
            else if ((!_authenticated) && (target.Authenticated))
                result = false;
            else if ((String.IsNullOrEmpty(target.UserName)) || (target.UserName == _userName))
                result = ((String.IsNullOrEmpty(target.RightName)) || (target.RightName == _rightName));
            else
                result = false;
            return result;
        }

        public Boolean IsSubsetOf(PrincipalPermission target)
        {
            Boolean result = false;


            if (target == null) return result;
            if (target.IsUnrestricted())
            {
                result = true;
            }
            else
            {

                SecurityElement element = target.ToXml();
                Boolean authencation = CheckSubsetAuthencation(element);
                Boolean userId = CheckSubsetUserId(element);
                Boolean roleId = CheckSubsetRole(element);

                foreach (SecurityElement item in element.Children)
                {

                    if (authencation) authencation = CheckSubsetAuthencation(item);
                    if (userId) userId = CheckSubsetUserId(item);
                    if (roleId) roleId = CheckSubsetRole(item);
                }
                result = authencation && userId && roleId;

            }

            return result;
        }

        private bool CheckSubsetRole(SecurityElement item)
        {
            Boolean result = true;
            String value = String.Empty;
            value = item.Attribute("Role");
            if (!String.IsNullOrEmpty(value))
            {
                result = RightMaster.Instance.GetRightsByRoleName(value).Count(r => r.RightName == value) > 0;
            }
            else
            {
                result = false;
            }
            return result;
        }

        private bool CheckSubsetUserId(SecurityElement item)
        {
            Boolean result = true;
            String value = String.Empty;
            value = item.Attribute("ID");
            if (value != null)
            {
                result = (String.IsNullOrEmpty(value) || value == _userName);
            }
            else
            {
                result = false;
            }
            return result;
        }

        private bool CheckSubsetAuthencation(SecurityElement item)
        {
            Boolean result = true;
            String value = String.Empty;
            value = item.Attribute("Authenticated");
            if (!String.IsNullOrEmpty(value))
            {
                result = String.Compare(value, "true", StringComparison.OrdinalIgnoreCase) == 0;
            }
            else
            {
                result = false;
            }
            return result;
        }

        public bool IsSubsetOf(IPermission target)
        {
            Boolean result = false;
            if (target == null) return result;

            if (target is RightPermission)
            {
                result = IsSubsetOf((RightPermission)target);
            }
            else if (target is PrincipalPermission)
            {
                result = IsSubsetOf((PrincipalPermission)target);
            }

            return result;
        }

        public IPermission Union(IPermission target)
        {
            IPermission result = null;
            if ((target == null) || (target is RightPermission && Equals((RightPermission)target)))
                result = Clone();
            else
                throw new NotSupportedException();

            return result;
        }

        #endregion

        #region ISecurityEncodable Members

        public override string ToString()
        {
            return ToXml().ToString();
        }
        public void FromXml(SecurityElement e)
        {
            switch ((String)e.Attributes["Version"])
            {
                case "1":
                default:
                    this.RightName = (String)e.Attributes["Role"];
                    this.UserName = (String)e.Attributes["User"];
                    this.Authenticated = ("Y" == (String)e.Attributes["Authenticated"]);
                    break;
            }
        }

        public SecurityElement ToXml()
        {
            SecurityElement element = new SecurityElement("IPermission");
            String typename = "ObjectHelpDesk.Security.RightPermission";
            element.AddAttribute("class", typename + ", " + this.GetType().Module.Assembly.FullName.Replace('"', '\''));
            element.AddAttribute("Version", "1");
            element.AddAttribute("Role", _rightName);
            element.AddAttribute("User", _userName);
            element.AddAttribute("Authenticated", _authenticated ? "Y" : "N");

            return element;
        }

        #endregion

        #region ICloneable Members

        public RightPermission Clone()
        {
            return (RightPermission)((ICloneable)this).Clone();
        }
        object ICloneable.Clone()
        {
            return this.MemberwiseClone();
        }

        #endregion
    }

}
